/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package drt;

import evaluation.Graph;
import evaluation.Vertex;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import logic.Type;

/**
 *
 * @author phong
 */
public class DrtMergeExpression extends DrtStandAloneExpression {
    
    DrtExpression first;
    DrtExpression second;
    
    public DrtMergeExpression(DrtExpression func, DrtExpression arg) {
        this.first = func;
        this.second = arg;
        
        if (!this.first.getType().matches(Type.TRUTH_TYPE) || !this.second.getType().matches(Type.TRUTH_TYPE))
            throw new Error("incorrect types");
    }
    
    public DrtExpression getFirst() {
        return this.first;
    }
    
    public DrtExpression getSecond() {
        return this.second;
    }
    
    @Override
    public Type getType() {
        return Type.TRUTH_TYPE;
    }
    
    /*
     * don't use this function
     */
    @Override
    public void setType(Type t) {
    }
    
    @Override
    public void getRefs(LinkedHashSet<DRSReferent> refs) {
        this.first.getRefs(refs);
        this.second.getRefs(refs);
    }
    
    @Override
    public void getAllVariables(LinkedHashSet<DrtVariable> vars) {
        this.first.getAllVariables(vars);
        this.second.getAllVariables(vars);
    }
    
    public List<DrtExpression> getAllComponents() {
        List<DrtExpression> fst = new LinkedList<DrtExpression>();
        List<DrtExpression> snd = new LinkedList<DrtExpression>();
        
        if (this.first instanceof DrtMergeExpression)
            fst = ((DrtMergeExpression)this.first).getAllComponents();
        else 
            fst.add(this.first);
        if (this.second instanceof DrtMergeExpression)
            snd = ((DrtMergeExpression)this.second).getAllComponents();
        else 
            snd.add(this.second);
        
        fst.addAll(snd);
        return fst;
    }

    @Override
    public DrtExpression simplify() {        
        DrtMergeExpression that = new DrtMergeExpression(this.first.simplify(), this.second.simplify());
        List<DrtExpression> comps = that.getAllComponents();
        
        for (int i = 0; i < comps.size(); i++) {
            comps.set(i, comps.get(i).simplify());
        }
        
        while (true) {
            boolean stop = true;
            
            for (int i = 0; i < comps.size() - 1; ) {
                DrtExpression f1 = comps.get(i);
                DrtExpression f2 = comps.get(i+1);
            
                if (f1 instanceof DRS && f2 instanceof DRS) {
                    stop = false;

                    List<DRSReferent> refs = new LinkedList<DRSReferent>(((DRS)f1).getRefs());
                    refs.addAll(((DRS)f2).getRefs());
                    List<DRSCondition> conds = new LinkedList<DRSCondition>(((DRS)f1).getConds());
                    conds.addAll(((DRS)f2).getConds());            
                    f1 = new DRS(refs, conds);    
                    comps.remove(i);
                    comps.set(i, f1);
                }
                else 
                    i++;
            }
            
            if (stop) break;
        }
        
        DrtExpression ret = comps.get(0);
        for (int i = 1 ; i < comps.size(); i++) 
            ret = new DrtMergeExpression(ret, comps.get(i));
        
        return ret;
    }
    
    @Override
    public DrtMergeExpression replace(DrtVariable var, DrtExpression exp) {
        return new DrtMergeExpression(
                first.replace(var, exp), 
                second.replace(var, exp));
    }
    
    @Override
    public String toString() {
        return "(" + this.first.toString() + ";" + this.second.toString() + ")";
    }
    
    @Override
    public boolean equals(Object other) {
        if (other instanceof DrtMergeExpression) {
            DrtMergeExpression that = (DrtMergeExpression)other;

            // alpha-convert
            LinkedHashSet<DrtVariable> thisVars = new LinkedHashSet<DrtVariable>();
            this.getAllVariables(thisVars);
            
            DrtMergeExpression repThat = (DrtMergeExpression)that.alphaConvert(thisVars);
            if (repThat == null) return false;
            
            return this.first.equals(repThat.first) && this.second.equals(repThat.second);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        int hc = "mergeexpression".hashCode();
        if (!(this.first instanceof DrtVariable))
            hc += this.first.hashCode() * 17;
        if (!(this.second instanceof DrtVariable))
            hc += this.second.hashCode() * 31;
        return hc;
    }

    @Override
    public Graph toGraph() {
        Graph g = new Graph();
        
        Graph g1 = this.first.toGraph();
        Graph g2 = this.second.toGraph();
        
        for (Vertex v : g1.getMarks()) {
            g.addMarks(v);
        }
        for (Vertex v : g2.getMarks()) {
            g.addMarks(v);
        }
        
        g.merge(g1);
        g.merge(g2);
        
        return g;
    }

    @Override
    public DrtMergeExpression getTemplate(Map<String,String> map) {
        DrtExpression newFirst = this.first.getTemplate(map);
        DrtExpression newSecond = this.second.getTemplate(map);
        return new DrtMergeExpression(newFirst, newSecond);
    }
}
